package com.mini.mybatis.builder.xml;

import com.mini.mybatis.mapping.BoundSql;
import com.mini.mybatis.mapping.MappedStatement;
import com.mini.mybatis.mapping.ParameterMapping;
import com.mini.mybatis.mapping.SqlCommandType;
import com.mini.mybatis.session.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * XML映射器构建器 - 解析Mapper.xml映射文件
 */
public class XMLMapperBuilder {
    
    private static final Logger logger = LoggerFactory.getLogger(XMLMapperBuilder.class);
    
    private final InputStream inputStream;
    private final Configuration configuration;
    private final String resource;
    
    public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource) {
        this.inputStream = inputStream;
        this.configuration = configuration;
        this.resource = resource;
    }
    
    /**
     * 解析映射文件
     */
    public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
            configurationElement(getDocument().getRootElement());
            configuration.addLoadedResource(resource);
        }
    }
    
    /**
     * 获取XML文档
     */
    private Document getDocument() {
        try {
            return new SAXReader().read(inputStream);
        } catch (DocumentException e) {
            logger.error("解析映射文件失败: " + resource, e);
            throw new RuntimeException("解析映射文件失败: " + resource, e);
        }
    }
    
    /**
     * 解析配置元素
     */
    private void configurationElement(Element element) {
        try {
            String namespace = element.attributeValue("namespace");
            if (namespace == null || namespace.isEmpty()) {
                throw new RuntimeException("映射文件中的namespace不能为空");
            }
            
            // 解析select、insert、update、delete语句
            buildStatementFromContext(element.elements("select"), namespace, SqlCommandType.SELECT);
            buildStatementFromContext(element.elements("insert"), namespace, SqlCommandType.INSERT);
            buildStatementFromContext(element.elements("update"), namespace, SqlCommandType.UPDATE);
            buildStatementFromContext(element.elements("delete"), namespace, SqlCommandType.DELETE);
            
        } catch (Exception e) {
            throw new RuntimeException("解析映射文件失败: " + resource + "，原因: " + e.getMessage(), e);
        }
    }
    
    /**
     * 从上下文构建语句
     */
    private void buildStatementFromContext(List<Element> list, String namespace, SqlCommandType sqlCommandType) {
        for (Element element : list) {
            parseStatementNode(namespace, element, sqlCommandType);
        }
    }
    
    /**
     * 解析语句节点
     */
    private void parseStatementNode(String namespace, Element element, SqlCommandType sqlCommandType) {
        String id = element.attributeValue("id");
        String parameterType = element.attributeValue("parameterType");
        String resultType = element.attributeValue("resultType");
        String sql = element.getTextTrim();
        
        // 构建完整的语句ID
        String statementId = namespace + "." + id;
        
        // 解析SQL语句，处理参数占位符
        SqlSource sqlSource = createSqlSource(sql, parameterType);
        BoundSql boundSql = sqlSource.getBoundSql(null);
        
        // 解析结果类型
        Class<?> resultTypeClass = null;
        if (resultType != null && !resultType.isEmpty()) {
            try {
                resultTypeClass = Class.forName(resultType);
            } catch (ClassNotFoundException e) {
                logger.warn("找不到结果类型: " + resultType);
            }
        }
        
        // 构建MappedStatement
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, statementId, sqlCommandType, boundSql)
                .resource(resource)
                .parameterType(parameterType)
                .resultType(resultTypeClass);
        
        MappedStatement statement = statementBuilder.build();
        configuration.addMappedStatement(statement);
    }
    
    /**
     * 创建SQL源
     */
    private SqlSource createSqlSource(String sql, String parameterType) {
        return new StaticSqlSource(configuration, sql);
    }
    
    /**
     * SQL源接口
     */
    private interface SqlSource {
        BoundSql getBoundSql(Object parameterObject);
    }
    
    /**
     * 静态SQL源实现
     */
    private static class StaticSqlSource implements SqlSource {
        
        private final String sql;
        private final List<ParameterMapping> parameterMappings;
        private final Configuration configuration;
        
        public StaticSqlSource(Configuration configuration, String sql) {
            this(configuration, sql, null);
        }
        
        public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
            this.sql = sql;
            this.configuration = configuration;
            this.parameterMappings = parameterMappings == null ? new ArrayList<>() : parameterMappings;
        }
        
        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return new BoundSql(configuration, sql, parameterMappings, parameterObject);
        }
    }
}